#include "stdafx.h"
#include "GraphicalMap.hpp"
#include "../graphical_actors/GraphicalActors.h"
#include "../graphical_actors/GraphicalCutit.hpp"

namespace graphicalenvironment
{
    static const int PARTICLE_BOURIK_SCENT_COLOUR = 0xFF0000FF;
    static const int PARTICLE_SPEEDBRICK_SCENT_COLOUR = 0xFFFFFF00;
    static const int PARTICLE_MIN_AGE = 100;
    static const int PARTICLE_MAX_AGE = 100;
    static const int PARTICLE_ANGLE = 0;
    static const irr::core::vector3df PARTICLE_INITIAL_DIRECTION = irr::core::vector3df(0.0f,1.0f,0.0f);
    static const irr::core::aabbox3d<irr::f32> PARTICLE_EMITTER_SIZE = irr::core::aabbox3d<irr::f32>(-MAP_TILE_SIZE/2.0f,0,-MAP_TILE_SIZE/2.0f,MAP_TILE_SIZE/2.0f,1,MAP_TILE_SIZE/2.0f);
    static const int PARTICLE_MIN_EMITTER_RATE = 100;
    static const int PARTICLE_MAX_EMITTER_RATE = 100;
    static const irr::core::dimension2df PARTICLE_MIN_SIZE = irr::core::dimension2df(10.f,10.f);
    static const irr::core::dimension2df PARTICLE_MAX_SIZE = irr::core::dimension2df(20.f,20.f);
    static const char PARTICLE_MATERIAL[] = "../media/textures/fire.bmp";

    GraphicalMap* GraphicalMap::m_Singleton = NULL;

    GraphicalMap::GraphicalMap() :
        m_mapGraphicalSpeedBricks(),
        m_mapGraphicalBouriks(),
        m_mapGraphicalCutits(),
        m_createdGraphicalGeneriks(0),
        m_aliveGraphicalGeneriks(0),
        m_createdGraphicalSpeedBricks(0),
        m_aliveGraphicalSpeedBricks(0),
        m_createdGraphicalBouriks(0),
        m_aliveGraphicalBouriks(0),
        m_vertexArray()
    {
        memset( m_tabGraphicalSpeedBrickScent ,0,sizeof(boost::uint8_t) * MAP_WIDTH*MAP_HEIGHT);
        memset( m_tabGraphicalBourikScent ,0,sizeof(boost::uint8_t) * MAP_WIDTH*MAP_HEIGHT);

        memset( m_tabGraphicalSpeedBrickScentParticles ,0,sizeof(irr::scene::IParticleSystemSceneNode *) * MAP_WIDTH*MAP_HEIGHT);
        memset( m_tabGraphicalBourikScentParticles ,0,sizeof(irr::scene::IParticleSystemSceneNode *) * MAP_WIDTH*MAP_HEIGHT);
    }

    GraphicalMap::~GraphicalMap()
    {
        Event::EventManager::getSingleton().destroyAllConnections( this );

        for (int i = 0; i < MAP_WIDTH*MAP_HEIGHT; i++ )
        {
            if ( m_tabGraphicalSpeedBrickScentParticles[i] )
            {
                m_tabGraphicalSpeedBrickScentParticles[i]->remove();
            }
            if ( m_tabGraphicalBourikScentParticles[i] )
            {
                m_tabGraphicalBourikScentParticles[i]->remove();
            }
        }

        for( std::map< int, graphicalactors::GraphicalSpeedBrick * >::iterator it_speedBrick = m_mapGraphicalSpeedBricks.begin();
            it_speedBrick != m_mapGraphicalSpeedBricks.end();
            ++it_speedBrick)
        {       
            delete it_speedBrick->second;
        }
        m_mapGraphicalSpeedBricks.clear();

        for( std::map< int, graphicalactors::GraphicalBourik * >::iterator it_bourik = m_mapGraphicalBouriks.begin();
            it_bourik != m_mapGraphicalBouriks.end();
            ++it_bourik)
        {
            delete it_bourik->second;
        }
        m_mapGraphicalBouriks.clear();

        for( std::map< int, graphicalactors::GraphicalCutit * >::iterator it_cutit = m_mapGraphicalCutits.begin();
            it_cutit != m_mapGraphicalCutits.end();
            ++it_cutit)
        {
            delete it_cutit->second;
        }
        m_mapGraphicalCutits.clear();
    }

    void GraphicalMap::addGraphicalSpeedBrick( graphicalactors::GraphicalSpeedBrick * _speedBrick )
    {
        std::pair< std::map< int, graphicalactors::GraphicalSpeedBrick * >::iterator,
            bool > it_insert = m_mapGraphicalSpeedBricks.insert( std::map< int, graphicalactors::GraphicalSpeedBrick * >::value_type( _speedBrick->getId(), _speedBrick ) );
        if ( it_insert.second )
        {
            ++m_createdGraphicalGeneriks;
            ++m_aliveGraphicalGeneriks;

            ++m_createdGraphicalSpeedBricks;
            ++m_aliveGraphicalSpeedBricks;
        }
        else
        {
            delete _speedBrick;
        }
    }

    void GraphicalMap::addGraphicalBourik( graphicalactors::GraphicalBourik * _bourik )
    {
        std::pair< std::map< int, graphicalactors::GraphicalBourik * >::iterator,
            bool > it_insert = m_mapGraphicalBouriks.insert( std::map< int, graphicalactors::GraphicalBourik *>::value_type( _bourik->getId(), _bourik ) );
        if ( it_insert.second )
        {
            ++m_createdGraphicalGeneriks;
            ++m_aliveGraphicalGeneriks;

            ++m_createdGraphicalBouriks;
            ++m_aliveGraphicalBouriks;
        }
        else
        {
            delete _bourik;
        }
    }

    void GraphicalMap::addGraphicalCutit( graphicalactors::GraphicalCutit * _cutit )
    {
        std::pair< std::map< int, graphicalactors::GraphicalCutit * >::iterator,
            bool > it_insert = m_mapGraphicalCutits.insert( std::map< int, graphicalactors::GraphicalCutit *>::value_type( _cutit->getId(), _cutit ) );
        if ( it_insert.second )
        {
        }
        else
        {
            delete _cutit;
        }
    }

    void GraphicalMap::removeGraphicalSpeedBrick( int _id )
    {
        std::map< int, graphicalactors::GraphicalSpeedBrick * >::iterator it_speedBrick = m_mapGraphicalSpeedBricks.find( _id );
        if ( it_speedBrick != m_mapGraphicalSpeedBricks.end() )
        {
            delete it_speedBrick->second;

            --m_aliveGraphicalGeneriks;
            --m_aliveGraphicalSpeedBricks;

            m_mapGraphicalSpeedBricks.erase( it_speedBrick );
        }
    }
    void GraphicalMap::removeGraphicalBourik( int _id )
    {
        std::map< int, graphicalactors::GraphicalBourik * >::iterator it_bourik = m_mapGraphicalBouriks.find( _id );
        if ( it_bourik != m_mapGraphicalBouriks.end() )
        {            
            delete it_bourik->second;

            --m_aliveGraphicalGeneriks;
            --m_aliveGraphicalBouriks;

            m_mapGraphicalBouriks.erase( it_bourik );
        }
    }

    void GraphicalMap::Init()
    {
        CONNECT(Event::EVENT_NEED_GENERIK_ADD, &GraphicalMap::onReceiveNeedGenerikAdd);
        CONNECT(Event::EVENT_NEED_GENERIK_REMOVE, &GraphicalMap::onReceiveNeedGenerikRemove);

        CONNECT(Event::EVENT_NEED_GENERIK_SCENT_ADD, &GraphicalMap::onReceiveNeedGenerikScentAdd);
        //CONNECT(Event::EVENT_NEED_GENERIK_SCENT_REMOVE, &GraphicalMap::onReceiveNeedGenerikScentRemove);

        m_vertexArray.resize( MAP_WIDTH * MAP_HEIGHT * 4 );

        for( int length_index = 0; length_index < MAP_HEIGHT; ++length_index )
        {
            int tile_position_length = length_index * MAP_TILE_SIZE;

            for( int width_index = 0; width_index < MAP_WIDTH; ++width_index )
            {
                int vertex_index = ( ( length_index * MAP_WIDTH ) + width_index ) * 4;
                int tile_position_width = width_index * MAP_TILE_SIZE;

                m_vertexArray[ vertex_index ] = irr::core::vector3df( tile_position_width, -10, tile_position_length );
                m_vertexArray[ vertex_index + 1 ] = irr::core::vector3df( tile_position_width + MAP_TILE_SIZE, -10, tile_position_length );
                m_vertexArray[ vertex_index + 2 ] = irr::core::vector3df( tile_position_width, -10, tile_position_length + MAP_TILE_SIZE );
                m_vertexArray[ vertex_index + 3 ] = irr::core::vector3df( tile_position_width + MAP_TILE_SIZE, -10, tile_position_length + MAP_TILE_SIZE );
            }
        }

        
        //create the particles
        core::Application * application = core::Application::getCurrentApplication();
        irr::scene::ISceneManager * sceneManager = application->getIrrlichtSceneManager();
        irr::video::IVideoDriver * driver = application->getIrrlichtVideoDriver();
        irr::scene::IParticleSystemSceneNode * particleSystem = NULL;
        irr::scene::IParticleEmitter * emitter = NULL;
        irr::scene::IParticleAffector * affector = NULL;
        int index = 0;
        for (int j = 0; j < MAP_HEIGHT; j++ )
        {
            for (int i = 0; i < MAP_WIDTH; i++ )            
            {
                index = GetIndexInTab(i*MAP_TILE_SIZE, j*MAP_TILE_SIZE);

                //speedBrick
                particleSystem = sceneManager->addParticleSystemSceneNode(false,
                                                                    0, 
                                                                    -1, 
                                                                    irr::core::vector3df(i * MAP_TILE_SIZE + MAP_TILE_SIZE/2.0f, 10, j * MAP_TILE_SIZE + MAP_TILE_SIZE/2.0f)
                                                                    );

                emitter = particleSystem->createBoxEmitter(
	                PARTICLE_EMITTER_SIZE, 
	                PARTICLE_INITIAL_DIRECTION,   
	                PARTICLE_MIN_EMITTER_RATE,
	                PARTICLE_MAX_EMITTER_RATE,                             
                    irr::video::SColor(PARTICLE_SPEEDBRICK_SCENT_COLOUR),       
	                irr::video::SColor(PARTICLE_SPEEDBRICK_SCENT_COLOUR),     
	                PARTICLE_MIN_AGE,
	                PARTICLE_MAX_AGE,
	                PARTICLE_ANGLE,                  
	                PARTICLE_MIN_SIZE,         
	                PARTICLE_MAX_SIZE);        

                particleSystem->setEmitter(emitter); 
                emitter->drop();

                affector = particleSystem->createFadeOutParticleAffector();
                particleSystem->addAffector(affector);
                affector->drop();

                particleSystem->setMaterialFlag(irr::video::EMF_LIGHTING, false);
                particleSystem->setMaterialFlag(irr::video::EMF_ZWRITE_ENABLE, false);
                particleSystem->setMaterialTexture(0, driver->getTexture(PARTICLE_MATERIAL));
                particleSystem->setMaterialType(irr::video::EMT_TRANSPARENT_ADD_COLOR);

                particleSystem->setVisible( false );

                m_tabGraphicalSpeedBrickScentParticles[index] = particleSystem;

                //bourik
                particleSystem = sceneManager->addParticleSystemSceneNode(false,
                                                                    0, 
                                                                    -1, 
                                                                    irr::core::vector3df(i * MAP_TILE_SIZE + MAP_TILE_SIZE/2.0f, 10, j * MAP_TILE_SIZE + MAP_TILE_SIZE/2.0f)
                                                                    );

                emitter = particleSystem->createBoxEmitter(
	                PARTICLE_EMITTER_SIZE, 
	                PARTICLE_INITIAL_DIRECTION,   
	                PARTICLE_MIN_EMITTER_RATE,
	                PARTICLE_MAX_EMITTER_RATE,                             
                    irr::video::SColor(PARTICLE_BOURIK_SCENT_COLOUR),       
	                irr::video::SColor(PARTICLE_BOURIK_SCENT_COLOUR),     
	                PARTICLE_MIN_AGE,
	                PARTICLE_MAX_AGE,
	                PARTICLE_ANGLE,                  
	                PARTICLE_MIN_SIZE,         
	                PARTICLE_MAX_SIZE);        

                particleSystem->setEmitter(emitter); 
                emitter->drop();

                affector = particleSystem->createFadeOutParticleAffector();
                particleSystem->addAffector(affector);
                affector->drop();

                particleSystem->setMaterialFlag(irr::video::EMF_LIGHTING, false);
                particleSystem->setMaterialFlag(irr::video::EMF_ZWRITE_ENABLE, false);
                particleSystem->setMaterialTexture(0, driver->getTexture(PARTICLE_MATERIAL));
                particleSystem->setMaterialType(irr::video::EMT_TRANSPARENT_ADD_COLOR);

                particleSystem->setVisible( false );

                m_tabGraphicalBourikScentParticles[index] = particleSystem;
            }
        }
    }

    void GraphicalMap::Update(irr::video::IVideoDriver* _driver)
    {
        _driver->setTransform(irr::video::ETS_WORLD, irr::core::IdentityMatrix);
        irr::video::SMaterial mat;
        mat.setTexture(0, _driver->getTexture("../media/textures/green.bmp"));
        mat.setFlag(irr::video::EMF_LIGHTING, false);
        _driver->setMaterial(mat);

        for( int length_index = 0; length_index < MAP_HEIGHT; ++length_index )
        {
            for( int width_index = 0; width_index < MAP_WIDTH; ++width_index )
            {
                int vertex_index = ( ( length_index * MAP_WIDTH ) + width_index ) * 4;

                irr::core::triangle3df tri1(
                    m_vertexArray[ vertex_index ],
                    m_vertexArray[ vertex_index + 2 ],
                    m_vertexArray[ vertex_index + 3 ]
                    );

                irr::core::triangle3df tri2(
                    m_vertexArray[ vertex_index ],
                    m_vertexArray[ vertex_index + 3 ],
                    m_vertexArray[ vertex_index + 1 ]
                    );

                _driver->draw3DTriangle( tri1 );
                _driver->draw3DTriangle( tri2 );
            }
        }
    }

    void GraphicalMap::addGraphicalSpeedBrickScent(float _posX, float _posZ, int _numToAdd )
    {
        if( _posX < 0 || _posX >= (MAP_WIDTH*MAP_TILE_SIZE) || _posZ < 0 || _posZ >= (MAP_HEIGHT*MAP_TILE_SIZE) )
            return;

        int index = GetIndexInTab(_posX,_posZ);

        if( m_tabGraphicalSpeedBrickScent[index]+_numToAdd <= 0 )
        {
            m_tabGraphicalSpeedBrickScent[index] = 0;

            if ( m_tabGraphicalSpeedBrickScentParticles[index] )
            {
                m_tabGraphicalSpeedBrickScentParticles[index]->setVisible( false );
            }
        }
        else
        {
            if ( m_tabGraphicalSpeedBrickScentParticles[index] )
            {
                m_tabGraphicalSpeedBrickScentParticles[index]->setVisible( true );
            }

            if( m_tabGraphicalSpeedBrickScent[index]+_numToAdd > 255 )
                m_tabGraphicalSpeedBrickScent[index] = 255;
            else
                m_tabGraphicalSpeedBrickScent[index] += _numToAdd;
        }

    }
    void GraphicalMap::addGraphicalBourikScent(float _posX, float _posZ, int _numToAdd )
    {
        if( _posX < 0 || _posX >= (MAP_WIDTH*MAP_TILE_SIZE) || _posZ < 0 || _posZ >= (MAP_HEIGHT*MAP_TILE_SIZE) )
            return;

        int index = GetIndexInTab(_posX,_posZ);

        if( m_tabGraphicalBourikScent[index]+_numToAdd < 0 )
        {
            m_tabGraphicalBourikScent[index] = 0;
            
            if ( m_tabGraphicalBourikScentParticles[index] )
            {
                m_tabGraphicalBourikScentParticles[index]->setVisible( false );
            }
        }
        else
        {
            if ( m_tabGraphicalBourikScentParticles[index] )
            {
                m_tabGraphicalBourikScentParticles[index]->setVisible( true );
            }

            if( m_tabGraphicalBourikScent[index]+_numToAdd > 255 )
                m_tabGraphicalBourikScent[index] = 255;
            else
                m_tabGraphicalBourikScent[index] += _numToAdd;
        }
    }

    int GraphicalMap::GetIndexInTab( float _posX, float _posZ )
    {
        if( _posX < 0 || _posX >= (MAP_WIDTH*MAP_TILE_SIZE) || _posZ < 0 || _posZ >= (MAP_HEIGHT*MAP_TILE_SIZE) )
            return (MAP_WIDTH*MAP_HEIGHT)-1;

        int temp = int(_posX/MAP_TILE_SIZE) + int(_posZ/MAP_TILE_SIZE) * MAP_WIDTH;

        if( temp < 0 )
            return 0;

        if( temp > (MAP_WIDTH*MAP_HEIGHT)-1 )
            return (MAP_WIDTH*MAP_HEIGHT)-1;

        return temp;
    }

    void GraphicalMap::onReceiveNeedGenerikAdd(boost::shared_ptr< Event::EventBase > _evt)
    {
        GET_REAL_EVENT_TYPE( Event::EventNeedGenerikAdd, evt, _evt );

        core::Application * currentApplication = core::Application::getCurrentApplication();
        irr::video::IVideoDriver * driver = currentApplication->getIrrlichtVideoDriver();
        irr::scene::ISceneManager * scnMgr = currentApplication->getIrrlichtSceneManager();
        graphicalactors::GraphicalGenerik * generik = NULL;
        graphicalactors::GraphicalCutit * cutit = NULL;

        switch ( evt->getType() )
        {
        case SPEEDBRICK:
            switch ( evt->getPhase() )
            {
            case NORMAL:
                generik = new graphicalactors::GraphicalNormalSpeedBrick( evt->getPosition());
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            case NICE:
                generik = new graphicalactors::GraphicalNiceSpeedBrick( evt->getPosition() );
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            case AGA:
                generik = new graphicalactors::GraphicalAGASpeedBrick( evt->getPosition() );
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            }
            generik->setId( evt->getIdToAdd() );
            addGraphicalSpeedBrick( (graphicalactors::GraphicalSpeedBrick*)generik );
            break;
        case BOURIK:
            switch ( evt->getPhase() )
            {
            case NORMAL:
                generik = new graphicalactors::GraphicalNormalBourik( evt->getPosition() );
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            case NICE:
                generik = new graphicalactors::GraphicalNiceBourik( evt->getPosition() );
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            case AGA:
                generik = new graphicalactors::GraphicalAGABourik( evt->getPosition() );
                generik->init( driver, scnMgr, evt->getAge() );
                break;
            }
            generik->setId( evt->getIdToAdd() );
            addGraphicalBourik( (graphicalactors::GraphicalBourik*)generik );
            break;
        case CUTIT:
            cutit = new graphicalactors::GraphicalCutit( evt->getPosition() );
            cutit->init( driver, scnMgr, evt->getAge() );
            cutit->setId( evt->getIdToAdd() );
            addGraphicalCutit( cutit );
            break;
            
        }
    }

    void GraphicalMap::onReceiveNeedGenerikRemove(boost::shared_ptr< Event::EventBase > _evt)
    {
        GET_REAL_EVENT_TYPE( Event::EventNeedGenerikRemove, evt, _evt );

        switch ( evt->getType() )
        {
        case SPEEDBRICK:
            removeGraphicalSpeedBrick( evt->getIdToRemove() );
            break;
        case BOURIK:
            removeGraphicalBourik( evt->getIdToRemove() );
            break;
        }
    }

    void GraphicalMap::onReceiveNeedGenerikScentAdd(boost::shared_ptr< Event::EventBase > _evt)
    {
        GET_REAL_EVENT_TYPE( Event::EventNeedGenerikScentAdd, evt, _evt );

        switch ( evt->getType() )
        {
        case SPEEDBRICK:
            addGraphicalSpeedBrickScent( evt->getPosX(), evt->getPosZ(), evt->getNumToAdd() );
            break;
        case BOURIK:
            addGraphicalBourikScent( evt->getPosX(), evt->getPosZ(), evt->getNumToAdd() );
            break;
        }
    }

    /*
    void GraphicalMap::onReceiveNeedGenerikScentRemove(boost::shared_ptr< Event::EventBase > _evt)
    {
        GET_REAL_EVENT_TYPE( Event::EventNeedGenerikScentRemove, evt, _evt );

        switch ( evt->getType() )
        {
        case SPEEDBRICK:
            addGraphicalSpeedBrickScent( evt->getPosX(), evt->getPosZ(), -evt->getNumToRemove() );
            break;
        case BOURIK:
            addGraphicalBourikScent( evt->getPosX(), evt->getPosZ(), -evt->getNumToRemove() );
            break;
        }
    }*/
}   //  namespace graphicalenvironment